package com.mspbots.core.teams.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.mspbots.core.AbstractBaseServiceImpl;
import com.mspbots.core.exception.BadParamsException;
import com.mspbots.core.teams.mapper.TeamsUserMapper;
import com.mspbots.core.teams.model.TeamsUser;
import com.mspbots.core.teams.service.TeamsUserService;
import com.mspbots.core.wise.model.TenantUser;
import com.mspbots.core.wise.model.TenantUserMapping;
import com.mspbots.core.wise.model.Users;
import com.mspbots.core.wise.service.TenantUserMappingService;
import com.mspbots.core.wise.service.TenantUserService;
import com.mspbots.core.wise.service.UsersService;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Log4j2
@Service
public class TeamsUserServiceImpl extends AbstractBaseServiceImpl<TeamsUserMapper, TeamsUser> implements TeamsUserService {

    private final TenantUserService tenantUserService;
    private final TenantUserMappingService mappingService;
    private final UsersService usersService;


    public TeamsUserServiceImpl(TenantUserService tenantUserService,
                                TenantUserMappingService mappingService,
                                UsersService usersService) {
        this.tenantUserService = tenantUserService;
        this.mappingService = mappingService;
        this.usersService = usersService;
    }

    @Override
    public List<TeamsUser> selectAllByTenantId(long tenantId) {
        return this.baseMapper.selectAllByTenantId(tenantId);
    }

    @Override
    public TeamsUser selectByTeamsUserId(String teamsUserId) {
        Assert.notNull(teamsUserId, "Teams User Id is can not be empty or null.");
        return this.baseMapper.selectOne(Wrappers.<TeamsUser>lambdaQuery()
                .eq(TeamsUser::getTeamsUserId, teamsUserId).last("limit 1"));
    }

    @Override
    public List<TeamsUser> addBatch(Long tenantId, List<TeamsUser> teamsUsers) {

        if (CollectionUtils.isEmpty(teamsUsers) || teamsUsers.size() == 0) {
            throw new BadParamsException("Teams user add batch params users is null.");
        }

        List<TeamsUser> allTeamsUser = this.list(Wrappers.<TeamsUser>lambdaQuery()
                .eq(TeamsUser::getTenantId, teamsUsers.get(0).getTenantId()));

        List<TeamsUser> updatedUsers = teamsUsers.parallelStream().peek(user -> {
            user.setTeamsOriginalMail(user.getTeamsMail());
            user.setTeamsUserPrincipalName(null);
            user.setSituation("active");
            TeamsUser teamsUser = allTeamsUser.stream()
                    .filter(x -> x.getTeamsUserId().equals(user.getTeamsUserId()))
                    .findFirst().orElse(null);
            if (!ObjectUtils.isEmpty(teamsUser)) {
                user.setId(teamsUser.getId());
                log.debug("Receiver teams users get update by id {}", user);
            }
        }).collect(Collectors.toList());

        List<String> syncUserIds = teamsUsers.parallelStream().map(TeamsUser::getTeamsUserId).collect(Collectors.toList());

        List<TeamsUser> inActiveUsers = allTeamsUser.parallelStream()
                .filter(dbUser -> !syncUserIds.contains(dbUser.getTeamsUserId()))
                .peek(item -> item.setSituation("inactive"))
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(updatedUsers)) {

            if (!CollectionUtils.isEmpty(inActiveUsers)) {
                updatedUsers.addAll(inActiveUsers);
            }

            this.saveOrUpdateBatch(updatedUsers);

            List<TenantUserMapping> mappings = new ArrayList<>();
            updatedUsers.parallelStream()
                    .filter(user -> user.getTeamsMail() != null)
                    .forEach(user -> {
                        TenantUserMapping mapping = autoMapping(user);
                        if (!ObjectUtils.isEmpty(mapping)) {
                            mapping.setEnable(user.getTeamsMail().contains("#EXT#") ? false : true);
                            mapping.setUserName(user.getTeamsMail());
                            mappings.add(mapping);
                        }
                    });

            Map<String, Long> users = usersService.list(Wrappers.<Users>lambdaQuery().eq(Users::getTenantId, tenantId))
                    .parallelStream().collect(Collectors.toMap(Users::getUsername, Users::getId));
            if (!CollectionUtils.isEmpty(mappings)) {
                mappings.parallelStream().filter(item -> item.getEnable() && StringUtils.isEmpty(item.getUserId()) && !StringUtils.isEmpty(users.get(item.getUserName())))
                        .forEach(item -> item.setUserId(users.get(item.getUserName())));
                this.mappingService.saveOrUpdateBatch(mappings);
            }
        }

        return updatedUsers;
    }

    @Override
    public Boolean autoMapping(Long tenantId) {
        List<TeamsUser> allTeamsUser = this.selectAllByTenantId(tenantId);
        List<TenantUserMapping> mappings = new ArrayList<>();

        allTeamsUser.parallelStream().forEach(teamsUser -> {
            TenantUserMapping mapping = autoMapping(teamsUser);
            if (!ObjectUtils.isEmpty(mapping)) {
                mappings.add(mapping);
            }
        });

        if (!CollectionUtils.isEmpty((mappings))) {
            this.mappingService.saveOrUpdateBatch(mappings);

            return true;
        }

        return false;
    }

    @Override
    public List<TeamsUser> findAdminByTenantId(long tenantId) {
        QueryWrapper<TeamsUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_admin", true);
        queryWrapper.eq("tenant_id", tenantId);
        return this.baseMapper.selectList(queryWrapper);
    }


    private TenantUserMapping autoMapping(TeamsUser teamsUser) {

        List<TenantUser> tenantUsers = this.tenantUserService.getTenantUsers(teamsUser.getTenantId(),
                teamsUser.getTeamsMail(), teamsUser.getTeamsOriginalMail());

        if (CollectionUtils.isEmpty(tenantUsers)) {
            return null;
        }

        if (tenantUsers.size() > 1) {
            log.error("found duplicated email {} in tenant [{}]", teamsUser.getTeamsMail(), teamsUser.getTenantId());
        }

        TenantUser tenantUser = tenantUsers.get(0);
        TenantUserMapping usersMapping = this.mappingService.getOne(Wrappers.<TenantUserMapping>lambdaQuery()
                .eq(TenantUserMapping::getTenantId, tenantUser.getTenantId())
                .eq(TenantUserMapping::getTenantUserId, tenantUser.getId()));

        if (!ObjectUtils.isEmpty(usersMapping)) {
            if (StringUtils.isEmpty(usersMapping.getTeamsUserId())) {
                usersMapping.setTeamsUserId(teamsUser.getTeamsUserId());
                return usersMapping;
            }
            return null;
        }

        usersMapping = new TenantUserMapping();
        usersMapping.setTenantId(tenantUser.getTenantId());
        usersMapping.setTenantUserId(tenantUser.getId());
        usersMapping.setTeamsUserId(teamsUser.getTeamsUserId());

        return usersMapping;
    }

}
